package org.xxx.system.interceptor;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.xxx.common.tool.JsonUtil;
import org.xxx.system.core.XXXRequestContext;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * 请求拦截
 */
@Slf4j
public class ApplicationRequestInterceptor implements HandlerInterceptor {

    /**
     * 在业务处理器处理请求之前被调用。预处理，可以进行编码、安全控制、权限校验等处理；
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        XXXRequestContext currentContext = XXXRequestContext.getCurrentContext();

        // 记录请求到达服务时间
        currentContext.setAcccptTimestamp();

        currentContext.setRequest(request);
        currentContext.setResponse(response);

        // 从请求中取出traceId并记录
        String traceId = currentContext.getTraceId();
        currentContext.setTraceId(traceId);

        // 从请求中取出父级spanId并记录
        String parentSpanId = currentContext.getHeader("spanId");
        currentContext.setParentSpanId(parentSpanId);

        // 生成服务自身spanId并记录
        String spanId = UUID.randomUUID().toString();
        currentContext.setSpanId(spanId);

        //日志打印
        //获取请求参数
        String uri = request.getRequestURI();
        //请求方法
        String httpMethod = request.getMethod();

        StringBuilder beforeReqLog = new StringBuilder(300);

        List<Object> paramList = new ArrayList();
        beforeReqLog.append("\n\n================  Request Start  ================\n");
        beforeReqLog.append("===> {}: {}");
        paramList.add(httpMethod);
        paramList.add(uri);
        this.logIngArgs(beforeReqLog, paramList);
        this.logIngHeaders(request, beforeReqLog, paramList);
        beforeReqLog.append("================   Request End   ================\n");


        log.info(beforeReqLog.toString(), paramList.toArray());

        return true;
    }


    /**
     * 在业务处理器处理请求执行完成后，生成视图之前执行。
     * 后处理（调用了Service并返回ModelAndView，但未进行页面渲染），有机会修改ModelAndView
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    /**
     * 在DispatcherServlet完全处理完请求后被调用，可用于清理资源等。返回处理（已经渲染了页面）；
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        XXXRequestContext currentContext = XXXRequestContext.getCurrentContext();

        // 服务响应时构建trace体
        JSONObject traceObject = new JSONObject();
        // 记录traceId
        traceObject.put("traceId", currentContext.getTraceId());

        // 记录父级spanId
        traceObject.put("parentSpanId", currentContext.getParentSpanId());

        // 记录服务spanId
        traceObject.put("spanId", currentContext.getSpanId());

        // 记录请求到达服务时间
        long acceptTimestamp = currentContext.getAcceptTimestamp();
        traceObject.put("acceptTimestamp", acceptTimestamp);

        // 记录请求响应时间
        long responseTimestamp = System.currentTimeMillis();
        traceObject.put("responseTimestamp", responseTimestamp);

        // 计算服务内请求耗时
        long costTimestamp = responseTimestamp - acceptTimestamp;
        traceObject.put("costTimestamp", costTimestamp);

        // 记录埋点信息
//        traceObject.put("tracePoint", currentContext.getTracePoint());


        log.info("\n\ntraceObject：\n" + JsonUtil.format(traceObject) + "\n");


        StringBuilder afterReqLog = new StringBuilder(200);
        List<Object> afterReqArgs = new ArrayList();

        Object obj = null;

        JSON.writeJSONString(response.getWriter(), obj);

        request.getAttribute("");


        System.out.println(obj.toString());


        afterReqLog.append("\n\n===============  Response Start  ================\n");
        afterReqLog.append("<=== result({} ms): {}  \n");
        afterReqArgs.add(costTimestamp);
        afterReqArgs.add(obj);
        afterReqLog.append("===============   Response End   ================\n");

        log.info(afterReqLog.toString(), afterReqArgs.toArray());

        // TODO 将trace体写入到MQ中

        XXXRequestContext.getCurrentContext().unset();
    }

    public void logIngArgs(StringBuilder beforeReqLog, List<Object> beforeReqArgs) {

        Map paraMap = new HashMap();
        String requestBodyValue = null;
        if (paraMap.isEmpty()) {
            beforeReqLog.append("\n");
        } else {
            beforeReqLog.append(" Parameters: {}\n");
            beforeReqArgs.add(JSONObject.toJSONString(paraMap));
        }


        if (requestBodyValue != null) {
            beforeReqLog.append("====Body=====  {}\n");
            beforeReqArgs.add(JSONObject.toJSONString(requestBodyValue));
        }

    }

    public void logIngHeaders(HttpServletRequest request, StringBuilder beforeReqLog, List<Object> beforeReqArgs) {
        Enumeration headers = request.getHeaderNames();
        while (headers.hasMoreElements()) {
            String headerName = (String) headers.nextElement();
            String headerValue = request.getHeader(headerName);
            beforeReqLog.append("===Headers===  {}: {}\n");
            beforeReqArgs.add(headerName);
            beforeReqArgs.add(headerValue);
        }
    }
}